package com.hup.joystick.service.joystick;

import com.hup.joystick.model.joystick.AutoConnectConfig;
import com.hup.joystick.model.joystick.NativeEvent;
import com.hup.joystick.model.joystick.StandardEvent;
import com.hup.joystick.service.jna.DIJoyState2;
import com.hup.joystick.service.jna.DInputHResultException;
import com.hup.joystick.service.jna.DeviceStateCallback;
import com.hup.joystick.service.jna.EnumDeviceData;
import com.hup.utils.commons.exception.AppCommonException;
import com.hup.utils.commons.exception.AppExceptionHandler;
import com.hup.utils.commons.thread.ThreadPoolUtil;
import com.hup.utils.commons.thread.ThreadUtil;
import lombok.extern.log4j.Log4j2;

import java.util.ArrayList;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * dll功能接口类,调用本类方法时应按需检查线程 {@link #notInUIThread()}
 *
 * @author hugan
 * @date 2020/10/20
 */
@Log4j2
public abstract class JoystickService implements DeviceStateCallback {

    protected final NativeEventParser nativeEventParser = new NativeEventParser();
    protected final StandardEventParser standardEventParser = new StandardEventParser();
    protected final ThreadPoolExecutor stateExecutor = ThreadPoolUtil.fixedThreadExecutor("JoystickState-", 1, "设备监听线程");
    protected final ThreadPoolExecutor autoConnectExecutor = ThreadPoolUtil.fixedThreadExecutor("Joystick-AutoConnect", 1, "设备连接线程");
    protected boolean listeningState;
    protected boolean autoConnecting;
    protected final ReentrantLock scanDeviceLock = new ReentrantLock();
    protected final Condition scanDeviceCondition = scanDeviceLock.newCondition();

    protected JoyEventListener joyEventListener;


    public abstract void initDirectInput() throws DInputHResultException;

    protected abstract void doFreeDirectInput();

    public abstract ArrayList<EnumDeviceData> enumDevices() throws DInputHResultException;

    /**
     * 在异步线程启动阻塞轮询监听, 设备状态或错误信息在 {@link DeviceStateCallback#onDeviceState(DIJoyState2, int)}中返回
     */
    public abstract void startListenState(String guid, int stateScanSpan, int stickRange,
            JoyEventListener joyEventListener);

    public abstract void startAutoConnect(AutoConnectConfig connectConfig, AutoConnectListener connectListener);

    /**
     * JNA回调
     * @param hr 监听过程中遇到的异常,如设备丢失;没有异常时为S_OK
     */
    @Override
    public void onDeviceState(DIJoyState2 state, int hr) {
        //log.info("receive:hr={}, state={}", HResult.getErrMsg(hr), state);
        if (joyEventListener != null) {
            try {
                NativeEvent nativeEvent = joyEventListener.parseNativeEvent() ? nativeEventParser.parse(state, hr) : null;
                StandardEvent standardEvent = standardEventParser.parse(state, hr, joyEventListener.getKeyMapConfig());
                joyEventListener.onJoyEvent(nativeEvent, standardEvent);
            } catch (Throwable e) {
                //这是JNA回调,必须捕获异常
                log.error("按键处理异常={}", AppExceptionHandler.handle(e).getErrMsg());
            }
        }
    }

    /**
     * 扫描设备轮询中的sleep方法,
     */
    protected void sleepScanDevice(int deviceScanSpan) {
        if (!autoConnecting) return;//处理调用stop后,刚好到达此处的情况
        try {
            scanDeviceLock.lock();
            scanDeviceCondition.await(deviceScanSpan, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("InterruptedException", e);
            Thread.currentThread().interrupt();
            throw new AppCommonException(e.toString());
        } finally {
            scanDeviceLock.unlock();
        }
    }

    /**
     * @param waitEndingThread true-等待线程结束后才返回
     */
    public void stopListenState(boolean waitEndingThread) {
        log.debug("listening={}", listeningState);
        listeningState = false;
        if (waitEndingThread) {
            while (stateExecutor.getActiveCount() != 0) {
                log.info("等待上一个监听线程结束");
                ThreadUtil.sleep(200);
            }
        }
    }

    public void stopAutoConnect(boolean waitEndingThread) {
        log.debug("autoConnecting={}, listeningState={}", autoConnecting, listeningState);
        autoConnecting = false;
        stopListenState(false);//跳出状态监听循环(如果有)
        //唤醒查找设备的await(如果有)
        scanDeviceLock.lock();
        scanDeviceCondition.signalAll();
        scanDeviceLock.unlock();

        if (waitEndingThread) {
            while (autoConnectExecutor.getActiveCount() != 0) {
                log.info("等待上一个监听线程结束");
                ThreadUtil.sleep(200);
            }
        }
    }

    /**
     * 可以多次调用
     */
    public void doFree() {
        log.info("st");
        doFreeDirectInput();
        stateExecutor.shutdownNow();
        autoConnectExecutor.shutdownNow();
        log.info("ed");
    }

    @Override
    protected void finalize() throws Throwable {
        log.info("");
        doFree();
        super.finalize();
    }

    /**
     * 如果在ui线程调用方法,则抛出异常
     */
    protected static void notInUIThread() {
        String threadName = Thread.currentThread().getName();
        if (threadName.startsWith("AWT") || threadName.startsWith("main")) {
            throw new RuntimeException("不能在ui线程调用方法");
        }
    }

}
